---@class QuestieLink
local QuestieLink = QuestieLoader:CreateModule("QuestieLink")
-------------------------
--Import modules
-------------------------
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
---@type QuestieLib
local QuestieLib = QuestieLoader:ImportModule("QuestieLib")
---@type QuestiePlayer
local QuestiePlayer = QuestieLoader:ImportModule("QuestiePlayer")
---@type TrackerUtils
local TrackerUtils = QuestieLoader:ImportModule("TrackerUtils")
---@type l10n
local l10n = QuestieLoader:ImportModule("l10n")

--- COMPATIBILITY ---
local GetQuestLink = QuestieCompat.GetQuestLink
local CALENDAR_WEEKDAY_NAMES = QuestieCompat.CALENDAR_WEEKDAY_NAMES
local CALENDAR_FULLDATE_MONTH_NAMES = QuestieCompat.CALENDAR_FULLDATE_MONTH_NAMES

QuestieLink.lastItemRefTooltip = ""

-- Forward declaration
local _AddQuestTitle, _AddQuestStatus, _AddQuestDescription, _AddQuestRequirements, _GetQuestStarter, _GetQuestFinisher, _AddPlayerQuestProgress
local _AddTooltipLine, _AddColoredTooltipLine


local oldItemSetHyperlink = ItemRefTooltip.SetHyperlink
--- Override of the default SetHyperlink function to filter Questie links
---@param link string
function ItemRefTooltip:SetHyperlink(link, ...)
    local questiePrefix, questId = string.match(link, "(questie):(%d+):")
    local isQuestieLink = questiePrefix == "questie"

    if (not ItemRefTooltip:IsShown()) then
        QuestieLink.lastItemRefTooltip = ""
    else
        QuestieLink.lastItemRefTooltip = QuestieLink.lastItemRefTooltip or link
    end

    if isQuestieLink and questId then
        Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieTooltips:ItemRefTooltip] SetHyperlink:", link)
        ShowUIPanel(ItemRefTooltip)
        ItemRefTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE");
        QuestieLink:CreateQuestTooltip(link)
        ItemRefTooltip:Show()

        local tooltipText = ItemRefTooltipTextLeft1:GetText()
        if QuestieLink.lastItemRefTooltip == tooltipText then
            ItemRefTooltip:Hide()
            QuestieLink.lastItemRefTooltip = ""
            return
        end

        QuestieLink.lastItemRefTooltip = tooltipText
        return
    else
        -- Make sure to call the default function so everything that is not Questie can be handled (item links e.g.)
        oldItemSetHyperlink(self, link, ...)
    end
end

---@return string
function QuestieLink:GetQuestLinkStringById(questId)
    local questName = QuestieDB.QueryQuestSingle(questId, "name");
    local questLevel, _ = QuestieLib.GetTbcLevel(questId);
    return QuestieLink:GetQuestLinkString(questLevel, questName, questId)
end

---@return string
function QuestieLink:GetQuestLinkString(questLevel, questName, questId)
    local questLink = GetQuestLink and GetQuestLink(questId)
    local questString = "["..questName.." ("..tostring(questId)..")]"

    if Questie.db.profile.trackerShowQuestLevel then
        questString = questString:gsub("%[", "[["..tostring(questLevel).."] ")
    end

    return questLink and questLink:gsub("%[(.-)%]", questString) or questString
end

---@return string
function QuestieLink:GetQuestHyperLink(questId, senderGUID)
    local coloredQuestName = QuestieLib:GetColoredQuestName(questId, Questie.db.profile.trackerShowQuestLevel, true, false)
    local questLevel, _ = QuestieLib.GetTbcLevel(questId)
    local isRepeatable = QuestieDB.IsRepeatable(questId)

    if (not senderGUID) then
        senderGUID = UnitGUID("player")
    end

    return "|Hquestie:"..questId..":"..senderGUID.."|h"..QuestieLib:PrintDifficultyColor(questLevel, "[", isRepeatable, QuestieDB.IsActiveEventQuest(questId), QuestieDB.IsPvPQuest(questId))..coloredQuestName..QuestieLib:PrintDifficultyColor(questLevel, "]", isRepeatable, QuestieDB.IsActiveEventQuest(questId), QuestieDB.IsPvPQuest(questId)).."|h"
end

function QuestieLink:CreateQuestTooltip(link)
    local isQuestieLink, _, _ = string.match(link, "questie:(%d+):.*")
    if isQuestieLink then
        ---@type string
        local questIdStr = select(2, strsplit(":", link))
        local questId = tonumber(questIdStr)
        local quest = QuestieDB.GetQuest(questId)

        if quest then
            _AddQuestTitle(quest)
            _AddQuestStatus(quest)

            _AddTooltipLine(" ")

            _AddQuestDescription(quest)
            _AddQuestRequirements(quest)
            local starterName, starterZoneName = _GetQuestStarter(quest)
            local finisherName, finisherZoneName = _GetQuestFinisher(quest)
            _AddPlayerQuestProgress(quest, starterName, starterZoneName, finisherName, finisherZoneName)
        end
    end
end

---@param text string
---@param wrapText boolean?
_AddTooltipLine = function (text, wrapText)
    ItemRefTooltip:AddLine(text, 1, 1, 1, wrapText)
end

---@param text string
---@param color string
---@param wrapText boolean?
_AddColoredTooltipLine = function (text, color, wrapText)
    text = Questie:Colorize(text, color)
    ItemRefTooltip:AddLine(text, 1, 1, 1, wrapText)
end

_AddQuestTitle = function(quest)
    local questLevel = QuestieLib:GetLevelString(quest.Id, quest.name, quest.level, false)

    local titleColor = "gold"
    if quest.specialFlags == 1 then
        titleColor = "blue"
    end

    if Questie.db.profile.trackerShowQuestLevel and Questie.db.profile.enableTooltipsQuestID then
        _AddColoredTooltipLine(questLevel .. quest.name .. " (" .. quest.Id .. ")", titleColor)
    elseif Questie.db.profile.trackerShowQuestLevel and (not Questie.db.profile.enableTooltipsQuestID) then
        _AddColoredTooltipLine(questLevel .. quest.name, titleColor)
    elseif Questie.db.profile.enableTooltipsQuestID and (not Questie.db.profile.trackerShowQuestLevel) then
        _AddColoredTooltipLine(quest.name .. " (" .. quest.Id .. ")", titleColor)
    else
        _AddColoredTooltipLine(quest.name, titleColor)
    end
end

_AddQuestStatus = function (quest)
    if QuestiePlayer.currentQuestlog[quest.Id] then
        local onQuestText = l10n("You are on this quest")
        local stateText
        local questIsComplete = QuestieDB.IsComplete(quest.Id)
        if questIsComplete == 1 then
            stateText = Questie:Colorize(l10n("Complete"), "green")
        elseif questIsComplete == -1 then
            stateText = Questie:Colorize(l10n("Failed"), "red")
        end

        if stateText then
            _AddTooltipLine(onQuestText .. " (" .. stateText .. ")")
        else
            _AddColoredTooltipLine(onQuestText, "green")
        end
    elseif Questie.db.char.complete[quest.Id] then
        _AddColoredTooltipLine(l10n("You have completed this quest"), "green")
    elseif (UnitLevel("player") < quest.requiredLevel or (not QuestieDB.IsDoable(quest.Id))) and (not Questie.db.char.hidden[quest.Id]) then
        _AddColoredTooltipLine(l10n("You are ineligible for this quest"), "red")
    elseif quest.specialFlags == 1 then
        _AddColoredTooltipLine(l10n("This quest is repeatable"), "yellow")
    else
        _AddColoredTooltipLine(l10n("You have not done this quest"), "yellow")
    end
end

_AddQuestDescription = function (quest)
    if quest and quest.Description and quest.Description[1] then
        _AddColoredTooltipLine(quest.Description[1], "white", true)
        if #quest.Description > 2 then
            for i = 2, #quest.Description do
                --_AddTooltipLine(" ") -- this is just adding extra lines between text definitions in DB files
                _AddColoredTooltipLine(quest.Description[i], "white", true)
            end
        end
    else
        _AddColoredTooltipLine(l10n("This quest is an automatic completion quest and does not contain an objective."), "white", true)
    end
end

_AddQuestRequirements = function (quest)
    if #quest.ObjectiveData > 0 and not (QuestiePlayer.currentQuestlog[quest.Id] or Questie.db.char.complete[quest.Id]) then
        for i = 1, #quest.ObjectiveData do
            local currentObjective = quest.ObjectiveData[i]
            if currentObjective then
                if currentObjective.Text then
                    if currentObjective == quest.ObjectiveData[1] then
                        _AddTooltipLine(" ")
                        _AddColoredTooltipLine(l10n("Requirements"), "gold")
                    end
                    _AddColoredTooltipLine(currentObjective.Text, "white")
                else
                    local objectiveName
                    if currentObjective.Type == "monster" then
                        objectiveName = QuestieDB.QueryNPCSingle(currentObjective.Id, "name")
                    else
                        objectiveName = QuestieDB.QueryItemSingle(currentObjective.Id, "name")
                    end

                    if objectiveName then
                        if currentObjective == quest.ObjectiveData[1] then
                            _AddTooltipLine(" ")
                            _AddColoredTooltipLine(l10n("Requirements"), "gold")
                        end
                        _AddColoredTooltipLine(objectiveName, "white")
                    end
                end
            end
        end
    end
end

_GetQuestStarter = function (quest)
    if quest.Starts then
        local starterName, starterZoneName
        if quest.Starts.NPC ~= nil then
            local npc = QuestieDB:GetNPC(quest.Starts.NPC[1])
            starterName = npc.name

            if npc.zoneID ~= 0 then
                starterZoneName = TrackerUtils:GetZoneNameByID(npc.zoneID)
            else
                starterZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
            end
        elseif quest.Starts.Item ~= nil then
            local item = QuestieDB:GetItem(quest.Starts.Item[1])
            starterName = item.name

            if item.Sources and item.Sources[1] and item.Sources[1].Type then
                local itemSource = item.Sources[1]
                local dropStart

                if itemSource.Type == "monster" then
                    dropStart = QuestieDB:GetNPC(itemSource.Id)
                elseif itemSource.Type == "object" then
                    dropStart = QuestieDB:GetObject(itemSource.Id)
                end

                if item.zoneID ~= 0 then
                    starterZoneName = TrackerUtils:GetZoneNameByID(dropStart.zoneID)
                else
                    starterZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
                end
            else
                starterZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
            end
        elseif quest and quest.Starts and quest.Starts.GameObject and quest.Starts.GameObject[1] then
            local object = QuestieDB:GetObject(quest.Starts.GameObject[1])
            starterName = object.name
            if object.zoneID ~= 0 then
                starterZoneName = TrackerUtils:GetZoneNameByID(object.zoneID)
            else
                starterZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
            end
        end

        return starterName, starterZoneName
    end

    return nil, nil
end

_GetQuestFinisher = function (quest)
    if quest.Finisher and quest.Finisher.Id then
        local finisherName, finisherZoneName
        if quest.Finisher.Type == "monster" then
            local npc = QuestieDB:GetNPC(quest.Finisher.Id)
            finisherName = npc.name

            if npc.zoneID ~= 0 then
                finisherZoneName = TrackerUtils:GetZoneNameByID(npc.zoneID)
            else
                finisherZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
            end
        elseif quest.Finisher.Type == "object" then
            local object = QuestieDB:GetObject(quest.Finisher.Id)
            finisherName = object.name

            if object.zoneID ~= 0 then
                finisherZoneName = TrackerUtils:GetZoneNameByID(object.zoneID)
            else
                finisherZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
            end
        else
            finisherZoneName = TrackerUtils:GetZoneNameByID(quest.zoneOrSort)
        end

        return finisherName, finisherZoneName
    end

    return nil, nil
end

_AddPlayerQuestProgress = function (quest, starterName, starterZoneName, finisherName, finisherZoneName)
    if QuestiePlayer.currentQuestlog[quest.Id] then
        -- On Quest: display quest progress
        if (QuestieDB.IsComplete(quest.Id) == 0) then
            _AddTooltipLine(" ")
            _AddTooltipLine(l10n("Your progress")..":")
            for _, objective in pairs(quest.Objectives) do
                local objDesc = objective.Description:gsub("%.", "")

                if objective.Needed > 0 then
                    local lineEnding = tostring(objective.Collected) .. "/" .. tostring(objective.Needed)
                    _AddTooltipLine(" - " .. QuestieLib:GetRGBForObjective(objective) .. objDesc .. ": " .. lineEnding.."|r")
                end
            end
        -- Completed Quest (not turned in): display quest ended by npc and zone
        else
            if finisherName then
                _AddTooltipLine(" ")
                _AddTooltipLine((l10n("Ended by")..": " .. Questie:Colorize(finisherName, "gray")))
            end
            if finisherZoneName then
                _AddTooltipLine(" ")
                _AddTooltipLine((l10n("Found in")..": " .. Questie:Colorize(finisherZoneName, "gray")))
            end
        end
    else
        -- Completed Quest (turned in)
        if Questie.db.char.complete[quest.Id] == true then
            if Questie.db.char.journey then
                local timestamp
                for i = 1, #Questie.db.char.journey do
                    if Questie.db.char.journey[i].Quest ~= nil and Questie.db.char.journey[i].Quest == quest.Id then
                        local year = tonumber(date("%Y", Questie.db.char.journey[i].Timestamp))
                        local day = CALENDAR_WEEKDAY_NAMES[ tonumber(date("%w", Questie.db.char.journey[i].Timestamp)) + 1 ]
                        local month = CALENDAR_FULLDATE_MONTH_NAMES[ tonumber(date("%m", Questie.db.char.journey[i].Timestamp)) ]
                        timestamp = Questie:Colorize(date( "[ "..day ..", ".. month .." %d, "..year.." @ %H:%M ]  " , Questie.db.char.journey[i].Timestamp), "blue")
                    end
                end
                if timestamp then
                    _AddTooltipLine(" ")
                    _AddTooltipLine(l10n("Completed on:"))
                    _AddTooltipLine(timestamp)
                end
            end
        -- Not on Quest: display quest started by npc and zone
        else
            if starterName then
                _AddTooltipLine(" ")
                _AddTooltipLine((l10n("Started by")..": " .. Questie:Colorize(starterName, "gray")))
            end
            if starterZoneName then
                _AddTooltipLine((l10n("Found in")..": " .. Questie:Colorize(starterZoneName, "gray")))
            end
        end
    end
end

hooksecurefunc("ChatFrame_OnHyperlinkShow", function(...)
    local _, link, _, button = ...
    if (IsShiftKeyDown() and ChatEdit_GetActiveWindow() and button == "LeftButton") then
        local linkType, questId, _ = string.split(":", link)
        if linkType and linkType == "questie" and questId then
            Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieTooltips:OnHyperlinkShow] Relinking Quest Link to chat:", link)
            questId = tonumber(questId)

            local quest = QuestieDB.GetQuest(questId)
            if quest then
                local msg = ChatFrame1EditBox:GetText()
                if msg then
                    ChatFrame1EditBox:SetText("")
                    ChatEdit_InsertLink(QuestieLink:GetQuestLinkString(quest.level, quest.name, questId))
                end
            end
        end
    end
end)
